//
//  CrashManager.m
//  flutter
//
//  Created by GilesZhou on 2020/6/28.
//  Copyright © 2020 Ganee. All rights reserved.
//

#import "CrashManager.h"
#import "CrashManagerProtocol.h"
#import <objc/runtime.h>
#import <MJExtension.h>
#import "RealmManager.h"
#import "RealmCrashModel.h"

@implementation CrashManager

+ (void)observeCrash {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
#warning 测试先不删除
//        [CrashManager uploadCrashLog];
        NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
        [NSArray ch_observeCrash];
        [NSString ch_observeCrash];
        [NSMutableArray ch_observeCrash];
    });
}

/// 崩溃信息收集
void UncaughtExceptionHandler(NSException *exception) {
    //堆栈数据
    NSArray *callStackSymbolsArr = [exception callStackSymbols];
    
    //获取在哪个类的哪个方法中实例化的数组  字符串格式 -[类名 方法名]  或者 +[类名 方法名]
    NSMutableDictionary *msgDic = [CrashManager getMainCallStackSymbolMessageWithCallStackSymbols:callStackSymbolsArr];
    NSString *mainCallStackSymbolMsg = msgDic[@"mainCallStackSymbolMsg"];
    
    if (mainCallStackSymbolMsg == nil) {
        mainCallStackSymbolMsg = @"崩溃方法定位失败,请您查看函数调用栈来排查错误原因";
    }
    
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    
    // 数据入库
    /// 打成model
    CrashModel *model = [[CrashModel alloc] init];
    model.functionData = [CrashFuncModel mj_objectWithKeyValues:msgDic];
    
    RealmCrashModel *realmModel = [[RealmCrashModel alloc] initWithValue:@[[model mj_JSONData], model.crashID]];;
    [RealmManager writeData:realmModel];
    
    NSLog(@"\n=====Crash======\nmethod:%@\nreason:%@\ncode:%@\ndata:%@\n============",mainCallStackSymbolMsg,reason,name, [model mj_keyValues]);
}

/// 类方法交换
+ (void)exchangeClassMethod:(Class)chClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel {
    Method method1 = class_getClassMethod(chClass, method1Sel);
    Method method2 = class_getClassMethod(chClass, method2Sel);
    method_exchangeImplementations(method1, method2);
}

/// 对象方法的交换
+ (void)exchangeInstanceMethod:(Class)chClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel {
    
    Method originalMethod = class_getInstanceMethod(chClass, method1Sel);
    Method swizzledMethod = class_getInstanceMethod(chClass, method2Sel);
    
    BOOL didAddMethod =
    class_addMethod(chClass,
                    method1Sel,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        class_replaceMethod(chClass,
                            method2Sel,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    }
    
    else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    
}



/// 上传崩溃信息
+ (void)uploadCrashLog {
    dispatch_queue_t queue = dispatch_queue_create("UploadCrashLogQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        for (RealmCrashModel *model in [RealmManager searchDataWithType:RealmSaveTypeCrash]) {
            
            [CrashManager requestUploadCrashLogWithModel:model];
        }
    });
}

/// 上传崩溃信息
+ (void)requestUploadCrashLogWithModel:(RealmCrashModel *)model {
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:model.saveData options:NSJSONReadingMutableLeaves error:nil];
    CrashModel *crashModel = [CrashModel mj_objectWithKeyValues:dic];
    
    ///TODO:上传后删除
    NSLog(@"%@", [crashModel mj_keyValues]);
    [RealmManager deleteData:model];
}


/// 崩溃栈分离调用者跟方法
+ (NSMutableDictionary *)getMainCallStackSymbolMessageWithCallStackSymbols:(NSArray<NSString *> *)callStackSymbols {
    
    //mainCallStackSymbolMsg的格式为   +[类名 方法名]  或者 -[类名 方法名]
    __block NSString *mainCallStackSymbolMsg = nil;
    __block NSString *mainClassName = nil;
    __block NSString *mainMethodName = nil;
    
    //匹配出来的格式为 +[类名 方法名]  或者 -[类名 方法名]
    NSString *regularExpStr = @"[-\\+]\\[.+\\]";
    
    NSRegularExpression *regularExp = [[NSRegularExpression alloc] initWithPattern:regularExpStr options:NSRegularExpressionCaseInsensitive error:nil];
    
    for (int index = 2; index < callStackSymbols.count; index++) {
        NSString *callStackSymbol = callStackSymbols[index];
        
        [regularExp enumerateMatchesInString:callStackSymbol options:NSMatchingReportProgress range:NSMakeRange(0, callStackSymbol.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
            if (result) {
                NSString* tempCallStackSymbolMsg = [callStackSymbol substringWithRange:result.range];
                
                //get className
                NSString *className = [tempCallStackSymbolMsg componentsSeparatedByString:@" "].firstObject;
                className = [className componentsSeparatedByString:@"["].lastObject;
                
                NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(className)];
                
                //filter category and system class
                if (![className hasSuffix:@")"] && bundle == [NSBundle mainBundle]) {
                    mainCallStackSymbolMsg = tempCallStackSymbolMsg;
                    mainClassName = className;
                    mainMethodName = [tempCallStackSymbolMsg componentsSeparatedByString:@" "].lastObject;
                    mainMethodName = [mainMethodName componentsSeparatedByString:@"]"].firstObject;
                }
                *stop = YES;
            }
        }];
        
        if (mainCallStackSymbolMsg.length) {
            break;
        }
    }
    NSMutableDictionary *resultDic = @{}.mutableCopy;
    NSMutableDictionary *methodDic = @{}.mutableCopy;
    methodDic[@"entityName"] = mainClassName;
    methodDic[@"methodName"] = mainMethodName;
    resultDic[@"scene"] = methodDic;
    resultDic[@"mainCallStackSymbolMsg"] = mainCallStackSymbolMsg;
    return resultDic;
}

@end
